package han.cirno.corrupt.activity.main

import android.graphics.BitmapFactory
import android.util.Base64
import androidx.activity.compose.LocalActivity
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import han.cirno.corrupt.R
import han.cirno.corrupt.components.refresh.BackgroundTask
import han.cirno.corrupt.compose.LocalLocaleContext
import han.cirno.corrupt.compose.VerticalSpacer
import han.cirno.corrupt.compose.localeStringResource
import han.cirno.corrupt.modules.classes.ClassesLayout
import han.cirno.corrupt.modules.exam.ExamLayout
import han.cirno.corrupt.modules.home.HomeLayout
import han.cirno.corrupt.modules.library.LibraryContentLayout
import han.cirno.corrupt.modules.library.LibraryLayout
import han.cirno.corrupt.modules.login.LoginLayout
import han.cirno.corrupt.modules.nofy.NofyLayout
import han.cirno.corrupt.modules.report.ReportLayout
import han.cirno.corrupt.modules.score.ScoreLayout
import han.cirno.corrupt.modules.setting.SettingConfig
import han.cirno.corrupt.modules.setting.SettingLayout
import han.cirno.corrupt.modules.theme.ThemeSettingLayout
import han.cirno.corrupt.util.ViewUtil.getString
import kotlinx.coroutines.launch

val LocalNavController= staticCompositionLocalOf<NavController> { error("No NavController found") }
val LocalBottomPadding= staticCompositionLocalOf { 0.dp }

@Composable
fun MainActivityLayout() {
    val mainActivityViewModel:MainActivityViewModel= hiltViewModel(LocalActivity.current as AppCompatActivity)
    val navController=rememberNavController()
    val currentBackStackEntry by navController.currentBackStackEntryAsState()
    val currentDestination=currentBackStackEntry?.destination?.route
    val subScreenList= NavItem.entries.asSequence().mapNotNull { if(it.subScreen)it.route else null }
    val paddingList= NavItem.entries.asSequence().mapNotNull { if(it.padding)it.route else null }
    val fullScreenList=NavItem.entries.asSequence().mapNotNull { if(it.fullScreen)it.route else null}.toList()
    val isSubScreen=currentDestination in subScreenList
    val isPadding=currentDestination in paddingList
    val isFullScreen=currentDestination in fullScreenList
    val backgroundEnabled by mainActivityViewModel.configEnableBackgroundState.collectAsState()
    val backgroundAlpha by mainActivityViewModel.configBackgroundAlphaState.collectAsState()
    val backgroundBase64 by mainActivityViewModel.configImageState.collectAsState()
    val backgroundByteArray = remember(backgroundBase64) {
        Base64.decode(backgroundBase64?:return@remember byteArrayOf(), Base64.DEFAULT)
    }
    val bitmapPainter= remember(backgroundByteArray) {
        if (backgroundByteArray.isEmpty())return@remember null
        BitmapPainter(BitmapFactory.decodeByteArray(backgroundByteArray,0,backgroundByteArray.size).asImageBitmap())
    }
    Scaffold (modifier=Modifier.fillMaxSize(),
        topBar = { },
        bottomBar = {
            AnimatedVisibility(
                visible=!isSubScreen,
                enter=fadeIn()+expandVertically(),
                exit=fadeOut()+shrinkVertically()
            ) { NavigationBar(navController) }})
    { innerPadding->
        AnimatedVisibility(visible = backgroundEnabled&&bitmapPainter!=null,
            enter = fadeIn(),
            exit = fadeOut()) {
            if (bitmapPainter==null)return@AnimatedVisibility
            Image(
                painter = bitmapPainter,
                contentDescription = null,
                contentScale = ContentScale.Crop,
                modifier = Modifier
                    .fillMaxSize()
                    .graphicsLayer { alpha = backgroundAlpha/1000f }
            )
        }
        Column  {
            AnimatedVisibility(visible = isPadding) {
                VerticalSpacer(innerPadding.calculateTopPadding())
            }
            AnimatedVisibility(visible = !isFullScreen,
                enter=fadeIn()+expandVertically(),
                exit=fadeOut()+shrinkVertically()) {
                Toolbar(navController)
            }
            Box(modifier = Modifier.weight(1f)) {
                NavHostContainer(innerPadding=innerPadding,navController = navController)
            }
            AnimatedVisibility(visible = isPadding) {
                VerticalSpacer(innerPadding.calculateBottomPadding())
            }
        }
    }
}

@Composable
fun NavHostContainer(innerPadding: PaddingValues,navController:NavHostController) {
    val viewModel:MainActivityViewModel= hiltViewModel(LocalActivity.current as AppCompatActivity)
    val language by viewModel.configLanguage.collectAsState()
    val localeContext= LocalLocaleContext.current
    val config by remember(language) {mutableStateOf(SettingConfig(localeContext,R.xml.prefs))}
    val scrollState=rememberScrollState()
    CompositionLocalProvider(values = arrayOf(
        LocalNavController provides navController,
        LocalBottomPadding provides innerPadding.calculateBottomPadding()
    )) {
        NavHost(
            navController = navController,
            startDestination = NavItem.Home.route
        ) {
            composable(NavItem.Home) { HomeLayout() }
            composable(NavItem.Settings) { SettingLayout(config, scrollState) }
            composable(NavItem.Classes) { ClassesLayout() }
            composable(NavItem.ExamsFunction) { ExamLayout() }
            composable(NavItem.NofyFunction) { NofyLayout() }
            composable(NavItem.LoginScreen) { LoginLayout() }
            composable(NavItem.LibraryFunction) { LibraryLayout() }
            composable(NavItem.LibraryDetailFunction) { LibraryContentLayout() }
            composable(NavItem.ThemeSettingScreen){ ThemeSettingLayout() }
            composable(NavItem.ScoreFunction){ ScoreLayout() }
            composable(NavItem.ReportScreen){ ReportLayout() }
        }
    }
}

@Composable
fun NavigationBar(navController:NavController) {
    val items=arrayOf(
        NavItem.Home,
        NavItem.Classes,
        NavItem.Settings
    )
    val mainActivityViewModel:MainActivityViewModel= hiltViewModel(LocalActivity.current as AppCompatActivity)
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    val navBarAlpha by mainActivityViewModel.configNavBarAlphaState.collectAsState()
    val currentRoute=navBackStackEntry?.destination?.route
    NavigationBar(
        modifier=Modifier
            .fillMaxWidth()
            .animateContentSize(),
        containerColor = MaterialTheme.colorScheme.surfaceContainer.copy(alpha = navBarAlpha/1000f)
    ) {
        items.forEach {item->
            NavigationBarItem(
                selected=currentRoute==item.route,
                icon={Icon(painterResource(item.icon),null)},
                label={Text(localeStringResource(item.route.substring(1).toInt()))},
                alwaysShowLabel=false,
                onClick={
                    navController.navigate(item.route) {
                        popUpTo(navController.graph.startDestinationId)
                        launchSingleTop=true
                    }
                }
            )
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Toolbar(navController: NavController){
    val currentBackStackEntry by navController.currentBackStackEntryAsState()
    val route=currentBackStackEntry?.destination?.route?:return
    val navItem=NavItem.findByRoute(route)
    val name=navItem.getRouteName()
    val viewModel:MainActivityViewModel= hiltViewModel(LocalActivity.current as AppCompatActivity)
    val loadingState by viewModel.loadingState
    val showHint = remember { Animatable(0f) }
    val showFinish = remember { Animatable(0f) }
    var trigger by remember { mutableIntStateOf(0) }
    val coroutineScope = rememberCoroutineScope()
    val loadingStatus=run{getLoadingStatus(loadingState)}
    val backgroundAlpha by viewModel.configAppBarAlphaState.collectAsState()
    LaunchedEffect(trigger) {
        showHint.animateTo(
            targetValue = 0f,
            animationSpec = tween(
                durationMillis = 1500,
                easing = LinearEasing
            )
        )
    }
    LaunchedEffect(loadingStatus.third) {
        if (loadingStatus.third)return@LaunchedEffect
        showFinish.snapTo(1f)
        showFinish.animateTo(
            targetValue = 0f,
            animationSpec = tween(
                durationMillis = 1500,
                easing = LinearEasing
            )
        )
        viewModel.loadingState.value=
            (getString(R.string.main_refresh_finished) to BackgroundTask.Status.None)
    }
    TopAppBar(
        title = {
            Column {
                Row {
                    AnimatedVisibility(
                        visible = navItem.subScreen&&navItem.icon!=0,
                        modifier = Modifier.padding(
                            end=if(navItem.subScreen)8.dp else 0.dp
                        )) {
                        Icon(
                            modifier = Modifier.size(24.dp),
                            painter = painterResource(
                                if(navItem.subScreen&&navItem.icon!=0) navItem.icon
                                else R.drawable.baseline_null_24),
                            contentDescription = null
                        )
                    }
                    Text(text = if(!navItem.fullScreen)name else "")
                }
                AnimatedVisibility(visible = loadingStatus.second||showFinish.value>0) {
                    Row(verticalAlignment = Alignment.CenterVertically) {
                        AnimatedVisibility(visible = loadingStatus.third) {
                            Row {
                                CircularProgressIndicator(
                                    modifier = Modifier.size(12.dp),
                                    strokeWidth = 2.dp
                                )
                                Spacer(modifier = Modifier.width(8.dp))
                            }
                        }
                        Text(
                            text=loadingStatus.first,
                            style = MaterialTheme.typography.bodyMedium
                        )
                    }
                }
                AnimatedVisibility(visible = showHint.value>0f&&!loadingStatus.second) {
                    Row(verticalAlignment = Alignment.CenterVertically) {
                        CircularProgressIndicator(modifier = Modifier.size(12.dp), progress = {showHint.value},
                            strokeWidth = 2.dp, gapSize = 0.dp, trackColor = Color.Transparent)
                        Spacer(modifier = Modifier.width(8.dp))
                        Text(
                            text= localeStringResource(R.string.main_refresh_clickAgain),
                            style = MaterialTheme.typography.bodyMedium
                        )
                    }
                }
            }
        },
        colors = TopAppBarDefaults.topAppBarColors(
            containerColor = MaterialTheme.colorScheme.surfaceContainer.copy(alpha = backgroundAlpha/1000f)),
        modifier = Modifier.combinedClickable(
            onClick = {
                if (loadingStatus.second)return@combinedClickable
                if (showHint.value>0f) {
                    viewModel.refresh()
                    coroutineScope.launch { showHint.snapTo(0f) }
                    return@combinedClickable
                }
                coroutineScope.launch { showHint.snapTo(1f) }
                trigger++
            }
        )
    )
}

@Composable
fun getLoadingStatus(it:Pair<String, BackgroundTask.Status>):Triple<String,Boolean,Boolean>{
    val suffixStatus=when(it.second){
        BackgroundTask.Status.None -> return Triple(it.first,false,true)
        BackgroundTask.Status.Loading -> localeStringResource(R.string.main_refresh_suffix_loading)
        BackgroundTask.Status.Finished -> localeStringResource(R.string.main_refresh_suffix_success)
        BackgroundTask.Status.Failed -> localeStringResource(R.string.main_refresh_suffix_failed)
        BackgroundTask.Status.Empty->""
        BackgroundTask.Status.AllDone->return Triple(it.first,false,false)
    }
    val isLoading=it.second== BackgroundTask.Status.Loading
    return Triple("${it.first} $suffixStatus" , isLoading,true)
}

enum class NavItem(
    val route: String,
    val icon: Int,
    val subScreen: Boolean = false,
    val fullScreen: Boolean = false,
    val padding: Boolean = false
) {
    Home(R.string.navigator_home,R.drawable.baseline_home_24),
    Classes(R.string.navigator_classes,R.drawable.baseline_dashboard_24),
    Tasks(R.string.navigator_tasks,R.drawable.baseline_edit_24),
    Settings(R.string.navigator_settings,R.drawable.baseline_settings_24),
    ExamsFunction(R.string.functions_exams, R.drawable.baseline_school_24, subScreen = true),
    ScoreFunction(R.string.functions_scores, R.drawable.baseline_star_24, subScreen = true),
    LibraryFunction(R.string.functions_fafuLib, R.drawable.baseline_library_books_24, subScreen = true),
    NofyFunction(R.string.functions_nofy, R.drawable.baseline_brightness_5_24, subScreen = true),
    LoginScreen("activity_login", 0, subScreen = true, fullScreen = true),
    LibraryDetailFunction("function_library_detail", 0, subScreen = true, fullScreen = true, padding = true),
    ThemeSettingScreen("sub_setting_theme", 0, subScreen = true, fullScreen=true, padding = true),
    ReportScreen(R.string.report_title,R.drawable.baseline_warning_24, subScreen = true);
    constructor(
        routeInt:Int,
        icon: Int,
        subScreen: Boolean = false,
        fullScreen: Boolean = false,
        padding: Boolean = false,
    ):this("@$routeInt", icon,subScreen,fullScreen,padding)

    @Composable
    fun getRouteName()=
        if(route.startsWith('@')) localeStringResource(route.substring(1).toInt()) else route
    companion object{
        fun findByRoute(route: String):NavItem=
            entries.find { it.route==route }?:throw IllegalArgumentException("Route $route not found")
    }
}

fun NavController.navigate(navItem: NavItem)=navigate(navItem.route)
fun NavGraphBuilder.composable(navItem: NavItem,content: @Composable (NavBackStackEntry) -> Unit)=
    composable(route=navItem.route,content=content)